﻿// <copyright file="CongressBill.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-05-28</date>
// <summary>CongressBill class definition</summary>

namespace NYTimes
{
    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Globalization;
    using System.Linq;
    using System.Xml.Linq;
    using NYTimes.Core;
    using NYTimes.Data;

    /// <summary>
    /// Bill Resources
    /// </summary>
    public enum BillResource
    {
        /// <summary>
        /// Bill subjects
        /// </summary>
        Subjects,

        /// <summary>
        /// Bill Amendments
        /// </summary>
        Amendments,

        /// <summary>
        /// Related bills
        /// </summary>
        Related
    }

    /// <summary>
    /// New York Times US Congress Bill API
    /// </summary>
    /// <remarks>see the 
    /// <a target="_blank" href="http://developer.nytimes.com/docs/congress_api#h3-bill-subjects">
    /// Bill Subjects, Amendments, and Related Bills</a> API for more information.</remarks> 
    public class CongressBill : CongressApi, INotifyPropertyChanged
    {
        #region Fields

        /// <summary>
        /// Congress number
        /// </summary>
        private int congressNumber;

        /// <summary>
        /// Bill result
        /// </summary>
        private Bill bill;

        /// <summary>
        /// the resource to retrieve
        /// </summary>
        private BillResource resource = BillResource.Subjects;

        /// <summary>
        /// the bill ID
        /// </summary>
        private string billId;

        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the CongressBill class
        /// </summary>
        public CongressBill()
        {
            this.Amendments = new ObservableCollection<Amendment>();

            this.RelatedBills = new ObservableCollection<Bill>();

            this.Subjects = new ObservableCollection<string>();

            if (SampleData.IsInDesignTool)
            {
                var xml = NYTimes.Core.SampleData.GetSampleData("BillSubjects.xml");

                this.ParseXml(xml);
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the congress number
        /// </summary>
        [Description("Gets or sets the congress number (105-111)")]
        public int CongressNumber
        {
            get
            {
                return this.congressNumber;
            }

            set
            {
                if (this.congressNumber != value)
                {
                    this.congressNumber = value;

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets or sets the bill resource
        /// </summary>
        [Description("Gets or sets the bill resource type to return (Subjects, Amendements, or Related)")]
        public BillResource Resource
        {
            get
            {
                return this.resource;
            }

            set
            {
                if (this.resource != value)
                {
                    this.resource = value;

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets or sets the Bill Id
        /// </summary>
        /// <remarks>Bill ID numbers are included in 
        /// <a href="http://developer.nytimes.com/docs/congress_api#h3-bills">bill summary</a> responses as url_number</remarks>
        [Description("Gets or sets the Bill Id")]
        public string BillId
        {
            get
            {
                return this.billId;
            }

            set
            {
                if (this.billId != value)
                {
                    this.billId = value;

                    this.FetchResults();
                }
            }
        }

        /// <summary>
        /// Gets the bill information
        /// </summary>
        public Bill Bill
        {
            get
            {
                return this.bill;
            }

            private set
            {
                if (this.bill != value)
                {
                    this.bill = value;

                    this.TriggerPropertyChanged("Bill");
                }
            }
        }

        /// <summary>
        /// Gets the ammendments on the bill
        /// </summary>
        public ObservableCollection<Amendment> Amendments { get; private set; }

        /// <summary>
        /// Gets the subjects that the bill is about
        /// </summary>
        public ObservableCollection<string> Subjects { get; private set; }

        /// <summary>
        /// Gets related bills
        /// </summary>
        public ObservableCollection<Bill> RelatedBills { get; private set; }
        #endregion

        #region Methods

        /// <summary>
        /// Determines whether to fetch the results
        /// </summary>
        /// <returns>true if the CongressNumber &gt; 0, the BillId is valid</returns>
        public override bool ShouldFetchResults()
        {
            if (this.CongressNumber < 1)
            {
                return false;
            }

            if (string.IsNullOrEmpty(this.BillId))
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// clear the Bill, amendments, subjects, and related bills
        /// </summary>
        public override void ClearResults()
        {
            this.Bill = null;

            this.Amendments.Clear();

            this.Subjects.Clear();

            this.RelatedBills.Clear();
        }

        /// <summary>
        /// Parse the XML results
        /// </summary>
        /// <param name="results">the XML results</param>
        public override void OnParseResults(XElement results)
        {
            this.Bill = CreateBill(results);

            switch (this.Resource)
            {
                case BillResource.Amendments:
                    this.ParseAmendments(results);
                    break;

                case BillResource.Related:
                    this.ParseRelated(results);
                    break;

                case BillResource.Subjects:
                    this.ParseSubjects(results);
                    break;
            }
        }

        /// <summary>
        /// Generate the Service URL
        /// </summary>
        /// <param name="baseUri">the Base URI</param>
        /// <returns>the service URL</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "not valid for Silverlight.")]
        public override Uri GenerateUrl(string baseUri)
        {
            var uri = string.Format(
                CultureInfo.InvariantCulture,
                "{0}/{1}/bills/{2}/{3}.xml?api-key={4}",
                baseUri,
                this.CongressNumber,
                this.BillId,
                this.Resource.ToString().ToLower(CultureInfo.InvariantCulture),
                this.ApiKey);

            return new Uri(uri, UriKind.Absolute);
        }

        #endregion

        #region Implementation

        /// <summary>
        /// Create a related bill object
        /// </summary>
        /// <param name="results">a related bill XML element</param>
        /// <returns>a Bill object</returns>
        private static Bill CreateRelatedBill(XElement results)
        {
            var bill = new Bill()
            {
                Id = results.Element("bill").Value,
                Introduced = DateTime.Parse(results.Element("introduced_date").Value, CultureInfo.InvariantCulture),
                LatestMajorAction = results.Element("latest_major_action").Value,
                LatestMajorActionDate = DateTime.Parse(results.Element("latest_major_action_date").Value, CultureInfo.InvariantCulture),
                Relationship = results.Element("relationship").Value,
                Sponsor = results.Element("sponsor").Value,
                SponsorId = results.Element("sponsor_id").Value,
                Title = results.Element("title").Value,
                UrlNumber = results.Element("url_number").Value
            };

            return bill;
        }

        /// <summary>
        /// create a bill
        /// </summary>
        /// <param name="results">the results element</param>
        /// <returns>a new Bill object</returns>
        private static Bill CreateBill(XElement results)
        {
            var bill = new Bill()
            {
                Committees = results.Element("committees").Value.Split(new string[] { "; " }, StringSplitOptions.RemoveEmptyEntries),
                Congress = int.Parse(results.Element("congress").Value, CultureInfo.InvariantCulture),
                Cosponsors = int.Parse(results.Element("cosponsors").Value, CultureInfo.InvariantCulture),
                HousePassageVote = string.IsNullOrEmpty(results.Element("house_passage_vote").Value) ? new DateTime?() : DateTime.Parse(results.Element("house_passage_vote").Value, CultureInfo.InvariantCulture),
                Id = results.Element("bill").Value,
                Introduced = DateTime.Parse(results.Element("introduced_date").Value, CultureInfo.InvariantCulture),
                LatestMajorAction = results.Element("latest_major_action").Value,
                LatestMajorActionDate = DateTime.Parse(results.Element("latest_major_action_date").Value, CultureInfo.InvariantCulture),
                SenatePassageVote = string.IsNullOrEmpty(results.Element("senate_passage_vote").Value) ? new DateTime?() : DateTime.Parse(results.Element("senate_passage_vote").Value, CultureInfo.InvariantCulture),
                Sponsor = results.Element("sponsor").Value,
                SponsorId = results.Element("sponsor_id").Value,
                Title = results.Element("title").Value,
                UrlNumber = results.Element("url_number").Value
            };
            return bill;
        }

        /// <summary>
        /// Parse the amendements
        /// </summary>
        /// <param name="results">the results element</param>
        private void ParseAmendments(XElement results)
        {
            if (results.Element("amendments") != null)
            {
                var amendments = from amendment in results.Element("amendments").Elements("amendment")
                                 select new Amendment()
                                 {
                                     IntroducedDate = DateTime.Parse(amendment.Element("introduced_date").Value, CultureInfo.InvariantCulture),
                                     LatestMajorAction = amendment.Element("latest_major_action").Value,
                                     LatestMajorActionDate = DateTime.Parse(amendment.Element("latest_major_action_date").Value, CultureInfo.InvariantCulture),
                                     Number = amendment.Element("amendment_number").Value,
                                     SponsorId = amendment.Element("sponsor_id").Value,
                                     Title = amendment.Element("title").Value
                                 };

                foreach (var amendment in amendments)
                {
                    this.Amendments.Add(amendment);
                }
            }
        }

        /// <summary>
        /// Parse the subjects
        /// </summary>
        /// <param name="results">the results element</param>
        private void ParseSubjects(XElement results)
        {
            if (results.Element("subjects") != null)
            {
                var subjects = from subject in results.Element("subjects").Elements("subject")
                               select subject.Element("name").Value;

                foreach (var subject in subjects)
                {
                    this.Subjects.Add(subject);
                }
            }
        }

        /// <summary>
        /// Parse the related bills
        /// </summary>
        /// <param name="results">the results element</param>
        private void ParseRelated(XElement results)
        {
            if (results.Element("related_bills") != null)
            {
                var related = from relatedBill in results.Element("related_bills").Elements("bill")
                              select CreateRelatedBill(relatedBill);

                foreach (var relatedBill in related)
                {
                    this.RelatedBills.Add(relatedBill);
                }
            }
        }

        #endregion
    }
}
